package com.pybox.common.utils;

/**
 * Created by wuyingkai on 2018/9/20.
 */
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class JdbcTemplatePagination {

    private static final String PAGINATION_SQL_TEMPLATE = "SELECT * FROM (  (#baseSql#) )ROW_ LIMIT #start#,#end# ";

    public JdbcTemplatePagination() {
    }

    public static <T> Page<T> queryPage(final JdbcTemplate jdbcTemplate, String querySql, final List<Object> paramList, RowMapper<T> rowMapper, int pageNumber, int pageSize) {
        JdbcTemplatePagination.PaginationCallback paginationCallback = new JdbcTemplatePagination.PaginationCallback() {
            public Long getCount(String countSql) {
                Long count = (Long)jdbcTemplate.queryForObject(countSql, paramList.toArray(), Long.class);
                return count;
            }

            public String getPaginationSql(String baseSql, int start, int end) {
                String sql = "SELECT * FROM (  (#baseSql#) )ROW_ LIMIT #start#,#end# ";
                sql = sql.replaceAll("#baseSql#", baseSql).replaceAll("#end#", "?").replaceAll("#start#", "?");
                paramList.add(start);
                paramList.add(end);
                return sql;
            }

            public <T> List<T> queryContent(String paginationSql, RowMapper<T> rowMapper) {
                return jdbcTemplate.query(paginationSql, paramList.toArray(), rowMapper);
            }
        };
        return queryPage(paginationCallback, querySql, rowMapper, pageNumber, pageSize);
    }

    public static <T> Page<T> queryPage(final NamedParameterJdbcTemplate namedParameterJdbcTemplate, String querySql, final Map<String, Object> paramMap, RowMapper<T> rowMapper, int pageNumber, int pageSize) {
        JdbcTemplatePagination.PaginationCallback paginationCallback = new JdbcTemplatePagination.PaginationCallback() {
            public Long getCount(String countSql) {
                Long count = (Long)namedParameterJdbcTemplate.queryForObject(countSql, paramMap, Long.class);
                return count;
            }

            public String getPaginationSql(String baseSql, int start, int end) {
                String sql = "SELECT * FROM (  (#baseSql#) )ROW_ LIMIT #start#,#end# ";
                sql = sql.replaceAll("#baseSql#", baseSql).replaceAll("#end#", ":end").replaceAll("#start#", ":start");
                paramMap.put("start", start);
                paramMap.put("end", end);
                return sql;
            }

            public <T> List<T> queryContent(String paginationSql, RowMapper<T> rowMapper) {
                return namedParameterJdbcTemplate.query(paginationSql, paramMap, rowMapper);
            }
        };
        return queryPage(paginationCallback, querySql, rowMapper, pageNumber, pageSize);
    }

    private static <T> Page<T> queryPage(JdbcTemplatePagination.PaginationCallback paginationCallback, String querySql, RowMapper<T> rowMapper, int pageNumber, int pageSize) {
        String countSql = getCountSql(querySql);
        Long count = paginationCallback.getCount(countSql);
        int pageCount = (int)Math.ceil((double)count / (double)pageSize);
        if (pageCount <= 0) {
            return new PageImpl(new ArrayList());
        } else {
            if (pageNumber < 0) {
                pageNumber = 0;
            }

            if (pageNumber > pageCount) {
                pageNumber = pageCount;
            }

            int start = pageNumber * pageSize;
            String paginationSql = paginationCallback.getPaginationSql(querySql, start, pageSize);
            List<T> content = paginationCallback.queryContent(paginationSql, rowMapper);
            Pageable pageable = new PageRequest(pageNumber, pageSize);
            Page<T> page = new PageImpl(content, pageable, count);
            return page;
        }
    }

    private static String getCountSql(String querySql) {
        return "select count(1) from (" + querySql + ") a";
    }

    private interface PaginationCallback {
        Long getCount(String var1);

        String getPaginationSql(String var1, int var2, int var3);

        <T> List<T> queryContent(String var1, RowMapper<T> var2);
    }
}
